﻿using System.Linq;
using System.Transactions;

namespace NQueries.Sample
{
    public static class SampleTransactions
    {
        /// <summary>
        /// Creates an object in a transaction and commits.
        /// </summary>
        public static int CreateInTransaction()
        {
            Test.CreateDatabase();

            using (IUnitOfWork unitOfWork = UnitOfWork.Begin(TransactionScopeOption.Required, "CreateInTransaction"))
            {
                unitOfWork.DataContext().Customers.Add(new Customer { Name = "CreateInTransaction", Address = new Address { Street = "CreateInTransaction" } });
                return unitOfWork.Commit();
            }
        }

        /// <summary>
        /// Creates two objects within the same transactions
        /// </summary>
        public static void CreateInNestedSameTransaction()
        {
            Test.CreateDatabase();

            using (IUnitOfWork outerUnitOfWork = UnitOfWork.Begin(TransactionScopeOption.Required, "CreateInNestedSameTransactionOuter"))
            {
                Customer customer = outerUnitOfWork.DataContext().Customers.Single(x => x.Name == "test");
                customer.Name = "new";

                using (IUnitOfWork innerUnitOfWork = UnitOfWork.Begin(TransactionScopeOption.Required, "CreateInNestedSameTransactionInner"))
                {
                    Address address = innerUnitOfWork.DataContext().Addresses.Single(x => x.Street == "test");
                    address.Street = "new";
                    innerUnitOfWork.Commit();
                }
                Test.CustomerNotExists("new");
                Test.AddressExists("new");

                outerUnitOfWork.Commit();
            }

            Test.CustomerExists("new");
            Test.AddressExists("new");
        }

        /// <summary>
        /// Creates two objects within the same transactions, inner uow commits, outer not, no object created.
        /// </summary>
        public static void CreateInNestedSameTransactionOuterRollback()
        {
            Test.CreateDatabase();

            using (IUnitOfWork outerUnitOfWork = UnitOfWork.Begin(TransactionScopeOption.Required, "CreateInNestedSameTransactionOuterRollbackOuter"))
            {
                Customer customer = outerUnitOfWork.DataContext().Customers.Single(x => x.Name == "test");
                customer.Name = "new";

                using (IUnitOfWork innerUnitOfWork = UnitOfWork.Begin(TransactionScopeOption.Required, "CreateInNestedSameTransactionOuterRollbackInner"))
                {
                    Address address = innerUnitOfWork.DataContext().Addresses.Single(x => x.Street == "test");
                    address.Street = "new";
                    innerUnitOfWork.Commit();
                }
                Test.CustomerNotExists("new");
                Test.AddressExists("new");
            }

            Test.CustomerNotExists("new");
            Test.AddressNotExists("new");
        }

        /// <summary>
        /// Creates two objects within the same transactions, inner uow rolls back, outer tries to commit
        /// </summary>
        public static void CreateInNestedSameTransactionInnerRollback()
        {
            Test.CreateDatabase();

            using (IUnitOfWork outerUnitOfWork = UnitOfWork.Begin(TransactionScopeOption.Required, "CreateInNestedSameTransactionOuterRollbackOuter"))
            {
                Customer customer = outerUnitOfWork.DataContext().Customers.Single(x => x.Name == "test");
                customer.Name = "new";

                using (IUnitOfWork innerUnitOfWork = UnitOfWork.Begin(TransactionScopeOption.Required, "CreateInNestedSameTransactionOuterRollbackInner"))
                {
                    Address address = innerUnitOfWork.DataContext().Addresses.Single(x => x.Street == "test");
                    address.Street = "new";
                    //innerUnitOfWork.Commit(); no commit => rollback!
                }
                //Test.CustomerNotExists("new");
                //Test.AddressNotExists("new");
                outerUnitOfWork.Commit();
            }

            Test.CustomerNotExists("new");
            Test.AddressNotExists("new");
        }

        /// <summary>
        /// Creates two objects within 2 different transactions
        /// </summary>
        public static void CreateInNestedDifferentTransaction()
        {
            Test.CreateDatabase();

            using (IUnitOfWork outerUnitOfWork = UnitOfWork.Begin(TransactionScopeOption.RequiresNew, "CreateInNestedDifferentTransactionOuter"))
            {
                Customer customer = new Customer { Name = "CreateInNestedDifferentTransactionOuter" };
                outerUnitOfWork.DataContext().Customers.Add(customer);

                using (IUnitOfWork innerUnitOfWork = UnitOfWork.Begin(TransactionScopeOption.RequiresNew, "CreateInNestedDifferentTransactionInner"))
                {
                    customer.Address = new Address { Street = "CreateInNestedDifferentTransactionInner" };
                    innerUnitOfWork.DataContext().Addresses.Add(customer.Address);

                    innerUnitOfWork.Commit();
                }

                outerUnitOfWork.Commit();
            }
        }

        /// <summary>
        /// Creates two objects within two transactions, inner uow commits, outer not, inner object is persisted
        /// </summary>
        public static void CreateInNestedDifferentTransactionOuterRollback()
        {
            Test.CreateDatabase();

            using (IUnitOfWork outerUnitOfWork = UnitOfWork.Begin(TransactionScopeOption.Required, "CreateInNestedDifferentTransactionOuterRollbackOuter"))
            {
                Customer customer = outerUnitOfWork.DataContext().Customers.Single(x => x.Name == "test");
                customer.Name = "new";

                using (IUnitOfWork innerUnitOfWork = UnitOfWork.Begin(TransactionScopeOption.RequiresNew, "CreateInNestedDifferentTransactionOuterRollbackInner"))
                {
                    Address address = innerUnitOfWork.DataContext().Addresses.Single(x => x.Street == "test");
                    address.Street = "new";
                    innerUnitOfWork.Commit();
                }
                Test.CustomerNotExists("new");
                Test.AddressExists("new");
            }

            Test.CustomerNotExists("new");
            Test.AddressExists("new");
        }

        /// <summary>
        /// Creates two objects, first in transaction, second nested with supressed transaction.
        /// </summary>
        public static void CreateInNestedSuppressedTransaction()
        {
            Test.CreateDatabase();

            using (IUnitOfWork outerUnitOfWork = UnitOfWork.Begin(TransactionScopeOption.Required, "CreateInNestedSuppressedTransactionOuter"))
            {
                Customer customer = new Customer { Name = "CreateInNestedSuppressedTransactionOuter" };
                outerUnitOfWork.DataContext().Customers.Add(customer);

                using (IUnitOfWork innerUnitOfWork = UnitOfWork.Begin(TransactionScopeOption.Suppress, "CreateInNestedSuppressedTransactionInner"))
                {
                    customer.Address = new Address { Street = "CreateInNestedSuppressedTransactionInner" };
                    innerUnitOfWork.DataContext().Addresses.Add(customer.Address);

                    innerUnitOfWork.Commit();
                }

                outerUnitOfWork.Commit();
            }
        }
    }
}